/**!
  使用双向链表在javascript中实现似乎并没有提升多少效率
 */

function Node(val = null, exp = null, prev = null, next = null) {
  this.val = val
  this.exp = exp
  this.prev = prev
  this.next = next
}

/**
 * @param {number} timeToLive
 */
export var AuthenticationManager = function (timeToLive) {
  this.timeToLive = timeToLive
  this.tokenDict = new Map()
  // 创建头尾节点
  this.tail = new Node()
  this.head = new Node()
  this.head.next = this.tail
  this.tail.prev = this.head
}

/**
 * @param {string} tokenId
 * @param {number} currentTime
 * @return {void}
 */
AuthenticationManager.prototype.generate = function (tokenId, currentTime) {
  const node = new Node(tokenId, currentTime + this.timeToLive)
  this.tokenDict.set(tokenId, node)
  // 将新节点插入链表尾部
  this.setNodeToTail(node)
}

AuthenticationManager.prototype.setNodeToTail = function (node) {
  const last = this.tail.prev
  last.next = node
  node.prev = last
  this.tail.prev = node
  node.next = this.tail
}

/**
 * @param {string} tokenId
 * @param {number} currentTime
 * @return {void}
 */
AuthenticationManager.prototype.renew = function (tokenId, currentTime) {
  if (this.tokenDict.has(tokenId) && this.tokenDict.get(tokenId).exp > currentTime) {
    // 取出原节点并移到最后
    const node = this.tokenDict.get(tokenId)
    node.exp = currentTime + this.timeToLive
    const prev = node.prev
    const next = node.next
    prev.next = next
    next.prev = prev
    // 将新节点插入链表尾部
    this.setNodeToTail(node)
  }
}

/**
 * @param {number} currentTime
 * @return {number}
 */
AuthenticationManager.prototype.countUnexpiredTokens = function (currentTime) {
  let cur = this.head.next
  // 遍历链表
  while (cur !== this.tail) {
    if (cur.exp > currentTime) break
    this.tokenDict.delete(cur.val)
    this.head.next = cur.next
    cur.next.prev = this.head
    cur = cur.next
  }
  return this.tokenDict.size
}

/**
 * Your AuthenticationManager object will be instantiated and called as such:
 * var obj = new AuthenticationManager(timeToLive)
 * obj.generate(tokenId,currentTime)
 * obj.renew(tokenId,currentTime)
 * var param_3 = obj.countUnexpiredTokens(currentTime)
 */
